#include "stdafx.h"
#include "Keyboard.h"
#include "KeyMapping.h"


CKeyboard::CKeyboard()
{
	m_keymap.reset(new CKeyMapping);

	std::fill(&m_keys[0], &m_keys[eKey_Count], false);
	RegisterInputDevice();
}

CKeyboard::~CKeyboard()
{
	
}

void CKeyboard::ProcessMessage(const RAWKEYBOARD& msg)
{
	USHORT vkey = GetVirtualKey(msg);
	EKey key = m_keymap->MapVirtualKey(vkey);

	if (key != eKey_Unknown)
	{
		SInputEvent input_event;
		input_event.event_type = key;
		input_event.key_state = (msg.Flags & RI_KEY_BREAK) ? eKeyState_Released : eKeyState_Pressed;
		input_event.autorepeat = IsKeyPressed(key);
		input_event.character = MessageToCharacter(msg);
		input_event.value = 0;
		input_event.alt = IsKeyPressed(eKey_LAlt) || IsKeyPressed(eKey_RAlt);
		input_event.ctrl = IsKeyPressed(eKey_LCtrl) || IsKeyPressed(eKey_RCtrl);
		input_event.shift = IsKeyPressed(eKey_LShift) || IsKeyPressed(eKey_RShift);

		SetKeyState(input_event.event_type, input_event.key_state);
		NotifyEvent(input_event);
	}
}

USHORT CKeyboard::GetVirtualKey(const RAWKEYBOARD& msg) const
{
	USHORT vkey = msg.VKey;
	switch (msg.VKey)
	{
	case VK_SHIFT:
		vkey = MapVirtualKey(msg.MakeCode, MAPVK_VSC_TO_VK_EX);
		break;

	case VK_CONTROL:
		vkey = (msg.Flags & RI_KEY_E0) ? VK_RCONTROL : VK_LCONTROL;
		break;

	case VK_MENU:
		vkey = (msg.Flags & RI_KEY_E0) ? VK_RMENU : VK_LMENU;
		break;
	}
	return vkey;
}

void CKeyboard::RegisterInputDevice()
{
	RAWINPUTDEVICE keyboard;
	keyboard.usUsagePage = 0x01;
	keyboard.usUsage = 0x06;
	keyboard.dwFlags = 0;
	keyboard.hwndTarget = 0;

	RegisterRawInputDevices(&keyboard, 1, sizeof(RAWINPUTDEVICE));
}

char CKeyboard::MessageToCharacter(const RAWKEYBOARD& msg)
{
	byte keyboard_state[256];
	GetKeyboardState(keyboard_state);

	uint16 buf[2] = {0, 0};
	if (ToAscii(msg.VKey, msg.MakeCode, keyboard_state, buf, 0) == 1 && buf[0] < MAX_LATIN_CHARACTER)
	{
		char character = buf[0] & 0xff;
		return character;
	}
	return 0;
}

void CKeyboard::SetKeyState(EKey key, EKeyState key_state)
{
	assert(key < eKey_Count);

	switch (key_state)
	{
	case eKeyState_Pressed:
		m_keys[key] = true;
		break;

	case eKeyState_Released:
		m_keys[key] = false;
		break;

	default:
		assert(!"Wrong key state provided");
	}
}

bool CKeyboard::IsKeyPressed(EKey key) const
{
	assert(key < eKey_Count);
	return m_keys[key];
}